#include "semantic_analyzer.h"
#include "semantics_class.h"
#include "semantics_enum.h"
#include "semantics_method.h"
#include "semantics_model.h"
#include "semantics_field.h"
#include "semantics_property.h"
#include "semantics_simulation.h"
#include "main_container.h"
#include "errors_warnings.h"

extern MainContainer *CompilerData;

SemanticAnalyzer::SemanticAnalyzer() {
	this->contexts = new GenericTable<SemanticsContext>();
	this->RootNamespace = new SemanticsNamespace();	
	this->FClasses = new GenericTable<SemanticsClass>();
	this->FFields = new GenericTable<SemanticsField>();
	this->FProperties = new GenericTable<SemanticsProperty>();
	this->FEnums = new GenericTable<SemanticsEnum>();
	this->FMethods = new GenericTable<SemanticsMethod>();
	this->FModels = new GenericTable<SemanticsModel>();
	this->FSimulations = new GenericTable<SemanticsSimulation>();
	this->FSingleFields = new GenericTable<SemanticsFieldSingle>();
}

SemanticAnalyzer::~SemanticAnalyzer() {
	// TODO
}

void SemanticAnalyzer::Gather() {
#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("SemanticAnalyzer::Gather");
#endif
	SyntaxTreeNode * root = CompilerData->SyntaxTree->RootNode;
	if(root->LexUnitID != -1 || root->Production != Gr_ProgramBody__GppFile_ProgramBody)	{
		// return
	}

	// Gather all files
	for(int i = 0; i < root->Nodes->GetCount(); i++)
	{
		// if this is the right production...
		if(root->Nodes->GetData(i)->LexUnitID == -1 && root->Nodes->GetData(i)->Production == Gr_GppFile__UsingDirectives_FileBody_endOfFile)
		{
			SyntaxTreeNode *currentGppFile = root->Nodes->GetData(i);

			// get current context
			// child 0 - using directives
			SemanticsContext *currentContext = GetCurrentContext(currentGppFile->Nodes->GetData(0));

			// save context for future use
			int currentContextID = SaveContext(currentContext);

			// process file body with given current context
			// child 1 - file body
			GatherFromFileBody(currentGppFile->Nodes->GetData(1), currentContextID, this->RootNamespace);
			
			// 2 - end of file
			// no checking
		}
	}

#ifdef TRACE
	CompilerData->LogTrace->ReportExit("SemanticAnalyzer::Gather");
#endif
}



void SemanticAnalyzer::GatherFromFileBody(SyntaxTreeNode * nodeFileBody, int parentContext, SemanticsNamespace * parentNamespace)
{
#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("SemanticAnalyzer::GatherFromFileBody");
#endif
	
	// check for the production...
	if(nodeFileBody->LexUnitID != -1 || nodeFileBody->Production != Gr_FileBody__MainDefinition_FileBody)
	{
		// report error
		CompilerData->ErrorWarningReport->ReportError(&CompilerData->Units->GetLexicUnit(nodeFileBody->LexUnitID), ERR_SEM_GENERIC_SEMANTIC_ERROR);

#ifdef TRACE
		CompilerData->LogTrace->ReportExit("SemanticAnalyzer::GatherFromFileBody");
#endif
		return;
	}


	for(int i = 0; i < nodeFileBody->Nodes->GetCount(); i++)
	{
		// pass each node to GatherFromMainDefinition for processing...
		SyntaxTreeNode * currentNode = nodeFileBody->Nodes->GetData(i);
		if(currentNode->Production == Gr_Simulation__simulation_NAME_spLCBrac_SimulationBody_spRCBrac)
		{
			SemanticsSimulation * sim = GatherFromSimulation(currentNode, parentContext);
			parentNamespace->Simulations->Add(sim);
		}
		else
		{
			GatherFromMainDefinition(currentNode, parentContext, parentNamespace);
		}
	}

#ifdef TRACE
	CompilerData->LogTrace->ReportExit("SemanticAnalyzer::GatherFromFileBody");
#endif
}


void SemanticAnalyzer::GatherFromMainDefinition(SyntaxTreeNode * nodeMainDefinition, int parentContext, SemanticsNamespace * parentNamespace)
{
#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("SemanticAnalyzer::GatherFromMainDefinition");
#endif


	if(nodeMainDefinition->LexUnitID == -1 && nodeMainDefinition->Production == Gr_MainDefinition__NamespaceDefinition)
	{
		// process namespace, add to root namespace
		parentNamespace->Namespaces->Add(GatherFromNamespace(nodeMainDefinition, parentContext));
	}
	else if(nodeMainDefinition->LexUnitID == -1 && nodeMainDefinition->Production == Gr_MainDefinition__EnumDefinition)
	{
		// process enum, add to root namespace
		parentNamespace->Enums->Add(GatherFromEnum(nodeMainDefinition, parentContext));
	}
	else if(nodeMainDefinition->LexUnitID == -1 && nodeMainDefinition->Production == Gr_MainDefinition__ClassDefinition)
	{
		// process class, add to root namespace
		parentNamespace->Classes->Add(GatherFromClass(nodeMainDefinition, parentContext));
	}
	else if(nodeMainDefinition->LexUnitID == -1 && nodeMainDefinition->Production == Gr_MainDefinition__ModelDefinition)
	{
		// process model, add to root namespace
		parentNamespace->Models->Add(GatherFromModel(nodeMainDefinition, parentContext));
	}
	else if(nodeMainDefinition->LexUnitID == -1 && nodeMainDefinition->Production == Gr_MainDefinition__MethodDefinition)
	{
		// process method, add to root nmspc
		parentNamespace->Methods->Add(GatherFromMethod(nodeMainDefinition, parentContext));
	}
	else if(nodeMainDefinition->LexUnitID == -1 && nodeMainDefinition->Production == Gr_MainDefinition__FieldDefinition)
	{
		// process field, add to root nspc
		parentNamespace->Fields->Add(GatherFromField(nodeMainDefinition, parentContext));
	}
	else if(nodeMainDefinition->LexUnitID == -1 && nodeMainDefinition->Production == Gr_MainDefinition__PropertyDefinition)
	{
		// process field, add to root nspc
		parentNamespace->Properties->Add(GatherFromProperty(nodeMainDefinition, parentContext));
	}

#ifdef TRACE
	CompilerData->LogTrace->ReportExit("SemanticAnalyzer::GatherFromMainDefinition");
#endif
}



/*
	Gather data from the expression node.
*/
SemanticsExpression * SemanticAnalyzer::GatherFromExpression(SyntaxTreeNode * expressionNode)
{
	// Check if we have valid expression node.
	if(expressionNode == NULL)
		return NULL;

	// Get first expression node and go deeper.
	SemanticsExpression * se = new SemanticsExpression();
	if(expressionNode->LexUnitID == -1 && expressionNode->Production == Gr_Expression__Expression2_ExpressionAdds)
	{
		se->leftExpression = GatherFromExpression2(expressionNode->Nodes->GetData(0));
		se->rightExpression = GatherFromExpressionAdds(expressionNode->Nodes->GetData(1));
	}
	
	// Reorganize expression built and return it.
	//se->Reorganize();
	return se;
}


// TODO -> correct errors
/*
	Gather data from the expression2 node.
*/
SemanticsExpression * SemanticAnalyzer::GatherFromExpression2(SyntaxTreeNode * expressionNode)
{
	// Check if we have valid expression node.
	if(expressionNode == NULL)
		return NULL;

	// Get data from the expression node and go deeper.
	SemanticsExpression * se = new SemanticsExpression();
	if(expressionNode->LexUnitID == -1 && expressionNode->Production == Gr_Expression2__Expression3_Expression2Adds)
	{
		//se->leftExpression = GatherFromExpression3(expressionNode->Nodes->GetData(0));
		//se->rightExpression = GatherFromExpression2Adds(expressionNode->Nodes->GetData(1));
	}

	// Reorganize expression built and return it.
	//se->Reorganize();
	return se;
}


/*
	Gather data from the expressionadds node.
*/
SemanticsExpression * SemanticAnalyzer::GatherFromExpressionAdds(SyntaxTreeNode * expressionNode)
{
	// Check if we have valid expression node.
	if(expressionNode == NULL)
		return NULL;

	// Get data from the expression node and go deeper.
	SemanticsExpression * se = new SemanticsExpression();
	if(expressionNode->LexUnitID == -1 && expressionNode->Production == Gr_ExpressionAdds__ExpressionAdd_ExpressionAdds)
	{
		//se->leftExpression = GatherFromExpression3(expressionNode->Nodes->GetData(0));
		//se->rightExpression = GatherFromExpression2Adds(expressionNode->Nodes->GetData(1));
	}
	

	// Reorganize expression built and return it.
	//se->Reorganize();
	return se;
}

SemanticsDataType * SemanticAnalyzer::GatherFromDataType(SyntaxTreeNode * dataTypeNode)
{
	// TODO - temp
	return new SemanticsDataType();
}


int SemanticAnalyzer::GatherArrayDimensionsCount(SyntaxTreeNode * arrayDeclarationNode)
{
	// TODO - temp
	return -1;
}


SemanticsContext * SemanticAnalyzer::GetCurrentContext(SyntaxTreeNode * nodeUsings)
{
#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("SemanticAnalyzer::GetCurrentContext");
#endif
	
	// TODO


#ifdef TRACE
	CompilerData->LogTrace->ReportExit("SemanticAnalyzer::GetCurrentContext");
#endif
	return NULL;
}




int SemanticAnalyzer::SaveContext(SemanticsContext * context)
{
#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("SemanticAnalyzer::SaveContext");
#endif

	// TODO


#ifdef TRACE
	CompilerData->LogTrace->ReportExit("SemanticAnalyzer::SaveContext");
#endif
	return -1;
}



int SemanticAnalyzer::SaveContext(SemanticsContext * context, int parrentContext)
{
#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("SemanticAnalyzer::SaveContext");
#endif

	// TODO


#ifdef TRACE
	CompilerData->LogTrace->ReportExit("SemanticAnalyzer::SaveContext");
#endif
	return -1;
}



#ifdef DEBUG
void SemanticAnalyzer::DebugXMLOutput(FILE * fo, LexicUnits * lus)
{
	// Start tag
	fprintf(fo, "<semantic_analyzer>\n");

	// make output of contexts
	fprintf(fo, "<contexts>\n");

	for(int i = 0; i < this->contexts->GetCount(); i++)
	{
		SemanticsContext * sc = this->contexts->GetData(i);

		sc->DebugXMLOutput(fo);
	}

	fprintf(fo, "</contexts>\n");


	// make output of root namespace
	this->RootNamespace->DebugXMLOutput(fo);
	
	
	// End tag
	fprintf(fo, "</semantic_analyzer>\n");
}






void SemanticAnalyzer::DebugXMLOutputFlatten(FILE * fo, LexicUnits * lus)
{
	// Start tag
	fprintf(fo, "<semantic_analyzer_flatten_data>\n");

	// make output of contexts
	fprintf(fo, "<contexts>\n");

	for(int i = 0; i < this->contexts->GetCount(); i++)
	{
		SemanticsContext * sc = this->contexts->GetData(i);

		sc->DebugXMLOutput(fo);
	}

	fprintf(fo, "</contexts>\n");


	// make output of classes
	fprintf(fo, "<flatten_classes>\n");
	for(int i = 0; i < this->FClasses->GetCount(); i++)
	{
		SemanticsClass * sc = this->FClasses->GetData(i);
		sc->DebugXMLOutput(fo);
	}
	fprintf(fo, "</flatten_classes>\n");

	
	
	// TODO - delete flatten fields output

	// make output of fields
	fprintf(fo, "<flatten_fields>\n");
	for(int i = 0; i < this->FFields->GetCount(); i++)
	{
		SemanticsField * sf = this->FFields->GetData(i);
		sf->DebugXMLOutput(fo);
	}
	fprintf(fo, "</flatten_fields>\n");


	// make output of single fields
	fprintf(fo, "<flatten_single_fields>\n");
	for(int i = 0; i < this->FSingleFields->GetCount(); i++)
	{
		SemanticsFieldSingle * sfs = this->FSingleFields->GetData(i);
		sfs->DebugXMLOutput(fo);
	}
	fprintf(fo, "</flatten_single_fields>\n");


	// make output of properties
	fprintf(fo, "<flatten_properties>\n");
	for(int i = 0; i < this->FProperties->GetCount(); i++)
	{
		SemanticsProperty * sp = this->FProperties->GetData(i);
		sp->DebugXMLOutput(fo);
	}
	fprintf(fo, "</flatten_properties>\n");


	// make output of enums
	fprintf(fo, "<flatten_enums>\n");
	for(int i = 0; i < this->FEnums->GetCount(); i++)
	{
		SemanticsEnum * se = this->FEnums->GetData(i);
		se->DebugXMLOutput(fo);
	}
	fprintf(fo, "</flatten_enums>\n");


	// make output of methods
	fprintf(fo, "<flatten_methods>\n");
	for(int i = 0; i < this->FMethods->GetCount(); i++)
	{
		SemanticsMethod * sm = this->FMethods->GetData(i);
		sm->DebugXMLOutput(fo);
	}
	fprintf(fo, "</flatten_methods>\n");


	// make output of models
	fprintf(fo, "<flatten_models>\n");
	for(int i = 0; i < this->FModels->GetCount(); i++)
	{
		SemanticsModel * sm = this->FModels->GetData(i);
		sm->DebugXMLOutput(fo);
	}
	fprintf(fo, "</flatten_models>\n");



	// make output of simulations
	fprintf(fo, "<simulations>\n");

	for(int i = 0; i < this->FSimulations->GetCount(); i++)
	{
		SemanticsSimulation * ss = this->FSimulations->GetData(i);

		ss->DebugXMLOutput(fo);
	}

	fprintf(fo, "</simulations>\n");


	// End tag
	fprintf(fo, "</semantic_analyzer_flatten_data>\n");
}


/*
--- OLD - deprecated
void SemanticAnalyzer::DebugOutputNameFromTable(FILE * fo, GenericTable<int> * name, LexicUnits * lus)
{
	for(int i = 0; i < name->GetCount(); i++)
	{
		// this should work... if not, repair it and don't try to make it so compact... :)
		try
		{
			fprintf(fo, "%s", (lus->GetLexicUnitString(&(lus->GetLexicUnit(*(name->GetData(i)))))).c_str());
		}
		catch(...)
		{
			fprintf(fo, "ICE_ERROR");
		}
		
		if(i < name->GetCount() - 1)
		{
			fprintf(fo, ".");
		}
	}
}
*/

void SemanticAnalyzer::DebugOutputNameFromTable(FILE * fo, GenericTable<int> * name, LexicUnits * lus)
{
	for(int i = 0; i < name->GetCount(); i++)
	{
		// this should work... if not, repair it and don't try to make it so compact... :)
		try
		{
			fprintf(fo, "%s", lus->GetIdentifierString(*(name->GetData(i))).c_str());
		}
		catch(...)
		{
			fprintf(fo, "ICE_ERROR");
		}
		
		if(i < name->GetCount() - 1)
		{
			fprintf(fo, ".");
		}
	}
}

#endif 




void SemanticAnalyzer::FlattenHierarchy()
{
#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("SemanticAnalyzer::FlattenHierarchy");
#endif

	bool hasChanged = true;

	FlattenFromNamespace(this->RootNamespace);


	// perform additional flattening
	for(int i = 0; i < this->FClasses->GetCount(); i++)
	{
		FlattenFromClass(this->FClasses->GetData(i));
	}


	// flatten fields entirely
	for(int i = 0; i < this->FFields->GetCount(); i++)
	{
		FlattenFromMultField(this->FFields->GetData(i));
	}

#ifdef TRACE
	CompilerData->LogTrace->ReportExit("SemanticAnalyzer::FlattenHierarchy");
#endif
}


void SemanticAnalyzer::FlattenFromMultField(SemanticsField * currentField)
{
#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("SemanticAnalyzer::FlattenFromMultField");
#endif

	for(int i = 0; i < currentField->Variables->GetCount(); i++)
	{
		SemanticsFieldVariable * fv = currentField->Variables->GetData(i);
		SemanticsFieldSingle * sfs = new SemanticsFieldSingle();

		sfs->ArrayCount = fv->ArrayCount;
		sfs->CurrentContext = currentField->CurrentContext;
		
		// copy data type
		sfs->DataType->builtInType = currentField->DataType->builtInType;
		CopyNames(currentField->DataType->DataType, sfs->DataType->DataType);
		
		sfs->ModifierConst = currentField->ModifierConst;
		sfs->ModifierPrivate = currentField->ModifierPrivate;
		sfs->ModifierProtected = currentField->ModifierProtected;
		sfs->ModifierPublic = currentField->ModifierPublic;
		sfs->ModifierStatic = currentField->ModifierStatic;

		// destroy expression information in the original field variable...
		sfs->Expression = fv->Expression;
		fv->Expression = NULL;

		// copy name
		CopyNames(fv->Name, sfs->Name);
	}

#ifdef TRACE
	CompilerData->LogTrace->ReportExit("SemanticAnalyzer::FlattenFromMultField");
#endif
}



void SemanticAnalyzer::FlattenFromNamespace(SemanticsNamespace * currentNamespace)
{
#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("SemanticAnalyzer::FlattenFromNamespace");
#endif
	
	// Flatten classes
	for(int i = 0; i < currentNamespace->Classes->GetCount(); i++)
	{
		SemanticsClass * sc = currentNamespace->Classes->GetData(i);
		CopyNames(currentNamespace->Name, sc->Name);
		this->FClasses->Add(sc);
		
		currentNamespace->Classes->RemoveAtWithoutFreeMem(i--);
	}


	// Flatten fields
	for(int i = 0; i < currentNamespace->Fields->GetCount(); i++)
	{
		SemanticsField * sf = currentNamespace->Fields->GetData(i);
		for(int j = 0; j < sf->Variables->GetCount(); j++)
		{
			CopyNames(currentNamespace->Name, sf->Variables->GetData(j)->Name);
		}
		
		this->FFields->Add(sf);
		
		currentNamespace->Fields->RemoveAtWithoutFreeMem(i--);
	}


	// Flatten properties
	for(int i = 0; i < currentNamespace->Properties->GetCount(); i++)
	{
		SemanticsProperty * sp = currentNamespace->Properties->GetData(i);
		CopyNames(currentNamespace->Name, sp->Name);
		this->FProperties->Add(sp);
		
		currentNamespace->Properties->RemoveAtWithoutFreeMem(i--);
	}


	// Flatten enums
	for(int i = 0; i < currentNamespace->Enums->GetCount(); i++)
	{
		SemanticsEnum * se = currentNamespace->Enums->GetData(i);
		CopyNames(currentNamespace->Name, se->Name);
		this->FEnums->Add(se);
		
		currentNamespace->Enums->RemoveAtWithoutFreeMem(i--);
	}


	// Flatten methods
	for(int i = 0; i < currentNamespace->Methods->GetCount(); i++)
	{
		SemanticsMethod * sm = currentNamespace->Methods->GetData(i);
		CopyNames(currentNamespace->Name, sm->Name);
		this->FMethods->Add(sm);
		
		currentNamespace->Methods->RemoveAtWithoutFreeMem(i--);
	}


	// Flatten models
	for(int i = 0; i < currentNamespace->Models->GetCount(); i++)
	{
		SemanticsModel * sm = currentNamespace->Models->GetData(i);
		CopyNames(currentNamespace->Name, sm->Name);
		this->FModels->Add(sm);
		
		currentNamespace->Models->RemoveAtWithoutFreeMem(i--);
	}


	// Flatten simulations
	for(int i = 0; i < currentNamespace->Simulations->GetCount(); i++)
	{
		SemanticsSimulation * sim = currentNamespace->Simulations->GetData(i);
		CopyNames(currentNamespace->Name, sim->Name);
		this->FSimulations->Add(sim);
		
		currentNamespace->Simulations->RemoveAtWithoutFreeMem(i--);
	}


	// Flatten namespaces
	for(int i = 0; i < currentNamespace->Namespaces->GetCount(); i++)
	{
		SemanticsNamespace * sn = currentNamespace->Namespaces->GetData(i);
		CopyNames(currentNamespace->Name, sn->Name);

		FlattenFromNamespace(sn);
		
		currentNamespace->Namespaces->RemoveAt(i--);
	}

#ifdef TRACE
	CompilerData->LogTrace->ReportExit("SemanticAnalyzer::FlattenFromNamespace");
#endif
}


void SemanticAnalyzer::FlattenFromClass(SemanticsClass * currentClass)
{
#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("SemanticAnalyzer::FlattenFromClass");
#endif
	
	// Flatten classes
	for(int i = 0; i < currentClass->Classes->GetCount(); i++)
	{
		SemanticsClass * sc = currentClass->Classes->GetData(i);
		CopyNames(currentClass->Name, sc->Name);
		this->FClasses->Add(sc);
		
		currentClass->Classes->RemoveAtWithoutFreeMem(i--);
	}

	// Flatten enums
	for(int i = 0; i < currentClass->Enums->GetCount(); i++)
	{
		SemanticsEnum * se = currentClass->Enums->GetData(i);
		CopyNames(currentClass->Name, se->Name);
		this->FEnums->Add(se);

		currentClass->Enums->RemoveAtWithoutFreeMem(i--);
	}

#ifdef TRACE
	CompilerData->LogTrace->ReportExit("SemanticAnalyzer::FlattenFromClass");
#endif
}




void SemanticAnalyzer::CopyNames(GenericTable<int> * source, GenericTable<int> * destination)
{
#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("SemanticAnalyzer::CopyNames");
#endif

	for(int i = 0; i < source->GetCount(); i++)
	{
		int * tempData = new int;
		*tempData = * source->GetData(i);
		destination->Insert(tempData, i);
#ifdef TRACE
		CompilerData->LogTrace->ReportValue("tempData", *tempData);
#endif
	}

#ifdef TRACE
	CompilerData->LogTrace->ReportExit("SemanticAnalyzer::CopyNames");
#endif
}


void SemanticAnalyzer::FillNames()
{
#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("SemanticAnalyzer::FillNames");
#endif

	// TODO


#ifdef TRACE
	CompilerData->LogTrace->ReportExit("SemanticAnalyzer::FillNames");
#endif
}




// SEMANTIC VERIFICATION
void SemanticAnalyzer::Verify()
{
#ifdef TRACE
	CompilerData->LogTrace->ReportEntry("SemanticAnalyzer::Verify");
#endif

	// TODO


#ifdef TRACE
	CompilerData->LogTrace->ReportExit("SemanticAnalyzer::Verify");
#endif
}


